<?php
/**
 * Created by PhpStorm.
 * User: Qch
 * Date: 2016/8/13
 * Time: 20:52
 */

namespace J;

use J\MVC\CommonModule;
use J\MVC\Module;
use J\Util\Config;
use J\Util\Console;
use J\Web\Context;


/**
 * 应用程序入口
 *
 * @method Util\Language Language()
 * @method Util\Router Router()
 * @method Util\Console Console()
 * @method Util\Database Database()
 * @method Schema\Collection\Element\ElementFactory Element()
 * @method Engine Engine()
 *
 * @method Storage\ORM Orm(string $table)
 *
 */
class Application implements Independent, IConfigurable
{
    const KEY_NAMESPACE = 'Namespace';
    const KEY_CONSTANT  = 'Constant';
    const KEY_INTENT    = 'Intent';
    const KEY_SERVICE   = 'Service';
    const SERVICE_IFC   = null;
    const KEY_ROUTER    = 'Router';
    const KEY_ARGUMENTS = 'arguments';
    const KEY_CLASS     = 'class';
    const KEY_INTERFACE = 'interface';
    const KEY_CONFIG    = 'config';
    const KEY_OBJECT    = 'Object';

    use Single;
    use Configurable;

    const USER_EXCEPTION_BASE_CODE  = 3000;

    /**
     * @var Loader 加载器
     */
    protected $loader;

    /**
     * @var Module[] 模块管理器
     */
    protected $modules;

    /**
     * @var User 当前用户
     */
    protected $user;

    /**
     * @var Manager 服务管理器
     */
    protected $server;

    /**
     * @var Config 默认配置
     */
    protected $defaultConfig;


    /**
     * 启动整个处理程序
     *
     * @param Loader $loader 加载器
     */
    public function bootstrap(Loader $loader)
    {
        $this->loader = $loader;

        //初始化服务管理器
        $this->createService();

        //导入配置
        $this->defaultConfig = $this->createFileConfig(config_file_name(__LIB__), null);
        $this->config = $this->createFileConfig(config_file_name(CONFIG_DIR), $this->defaultConfig);
        $this->extractConstant();

        //配置加载器
        $this->extractNamespace();

        //初始化路由服务、语言服务
        $this->Language()->regDir($this->getDir());

        $loader->setErrorHandler(function ($error, $context) {
            $this->error($error, $context);
        });

        // 启动完成
        define('BOOTSTRAP', true);

        // 执行请求并输出（这是整个框架的核心控制部分）
        // 安装检查在这里进行
        $pathinfo = file_exists(LOCK) ? null : INSTALL_PATH;

        $context = new Context($pathinfo);
        $this->getIntent()->Request($context);

        // highlight_string(var_export($this->Console()->getTrace(), true));
    }

    /**
     * 命名空间注册
     *
     * @param string $ns namespace
     * @param string $dir dir
     */
    public function registerNamespace($ns, $dir)
    {
        $this->loader->addNamespace($ns, $dir);
    }

    /////////////////////////////意图处理机制///////////////////////////////////////

    /**
     * 按照PATHINFO处理请求
     *
     * @param Intent $intent
     * @param \Closure $callback
     * @return bool|null
     *
     */
    public function invoke(Intent $intent, \Closure $callback = null)
    {
        try {
            $processor = $this->Router()->dispatch($intent->getCommand());

            /**
             * 安全起见，处理器仅支持闭包，
             * 闭包直接执行返回 ReturnValue:[错误码，结果|错误信息]
             * 不支持数组形式的方法（如：[$objOrClass, $method]）
             */
            if ($processor instanceof \Closure)
                return $processor($intent);
            //
            if($callback) $callback($processor);

            // 保持模型名称一致性
            $module = ucfirst($intent->module);
            // 执行
            return $this->getModule($module)->invoke($intent);
        } catch (\Throwable $e) {
            // 增加错误输出过滤动作
            return intent_ajax($intent, false,
                htmlentities($e->getMessage(), ENT_QUOTES, 'utf-8')
            );
        }
    }

    /**
     * 内部通信机制
     * @return Intent
     */
    public function getIntent()
    {
        return new Intent($this);
    }

    /**
     * 获取模块对象
     * @param string $name 模块名称
     *
     * @return Module
     * @throws Exception
     */
    protected function getModule($name)
    {
        if(!isset($this->modules[$name])){
            try {
                $class = '\\Application\\'.ucfirst($name).'\\'.ucfirst($name);
                $this->modules[$name] = new $class($this, $name);
            } catch (\Throwable $e) {
                $this->modules[$name] =  new CommonModule($this, $name);
            }
        }
        return $this->modules[$name];
    }

    /////////////////////////////////服务器///////////////////////////////////////////

    /**
     * 获取服务
     * @param string $name 服务名称
     * @param array $arguments
     * @return mixed
     * @throws \Exception
     */
    public function __call($name, $arguments = null)
    {
        if (!empty($arguments))
            return $this->getObject($name, $arguments);
        return $this->getService($name);
    }

    /**
     * 获取服务
     * @param string $name 服务名称
     * @return mixed
     * @throws Exception
     */
    public function getService($name)
    {
        return $this->server->getObject($name);
    }

    /**
     * 获取实例
     * @param string $name
     * @param array $arguments
     * @return null|object
     * @throws Exception
     */
    public function getObject($name, $arguments = null)
    {
        $definition = $this->get(self::KEY_OBJECT . '.' . ucfirst($name), []);
        return $this->createObject($name, $definition, $arguments);
    }

    protected function createService()
    {
        $this->server = new SimpleManager(function ($name) {
            // 获取服务配置
            $definition = $this->get(self::KEY_SERVICE . '.' . ucfirst($name), []);
            return $this->createObject($name, $definition);
        },true);
    }

    /**
     * 创建对象
     *
     * @param string $name
     * @param array|string $definition
     * @param array $arguments
     * @return null|object
     * @throws Exception
     */
    protected function createObject($name, $definition, $arguments = null)
    {
        if (is_string($definition)) {
            $class = $definition;
            $interface = null;
        } else {
            // 类名称
            $class = array_isset($definition, self::KEY_CLASS, '\\J\\Util\\'.ucfirst($name));
            // 接口
            $interface = array_isset($definition, self::KEY_INTERFACE, null);
        }

        // TODO 在这里注册 namespace if (isset($definition['dir']));

        // 获取对象
        $obj = get_instance($class, $arguments, $interface);

        if (!$obj) throw new Exception(Message::SERVICE_NOT_FIND, compact('class'));

        if (
            $arguments === null and
            $this->get(ucfirst($name)) and
            method_exists($obj, 'setConfig')
        )
            $obj->setConfig($this->get(ucfirst($name)));

        return $obj;
    }

    ///////////////////////////////////接口实现///////////////////////////////////////

    public function getConfig()
    {
        return $this->config;
    }

    public function debug($message, $context = array(), $level = Console::DEBUG)
    {
        $r = $this->translate($message, $context);
        $this->Console()->log($r, $level);
        return $r;
    }

    public function error($message, $context = array())
    {
        return $this->debug($message, $context, Console::ERROR);
    }

    /**
     * 获取文件目录
     * @return string 配置文件、语言文件基本路径
     */
    public function getDir()
    {
        return __LIB__;
    }

    /**
     * 当前用户
     * @return User
     */
    public function User()
    {
        if (!$this->user)
            $this->user = new User(function ($information, $user) {
                return $this->getIntent()->fetch('UserCenter/Gateway/'.$information, ['user' => $user]);
            });

        return $this->user;
    }

    ///////////////////////////////////内部功能////////////////////////////////////////

    protected function extractConstant()
    {
        $constant = $this->get(self::KEY_CONSTANT);
        foreach ((array)$constant as $key => $value) {
            !is_string($key) or \defined($key) or \define($key, $value);
        }
    }

    protected function extractNamespace()
    {
        $namespace = $this->get(self::KEY_NAMESPACE, []);
        array_walk($namespace,function($value, $key){
            $this->registerNamespace($key,$value);
        });
    }

    protected function translate($message, $context = array())
    {
        return $this->Language()->translation(message($message, $context));
    }

}